﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Web;
using SecurityClient;

namespace SecurityModule.Workflows
{
    class WFPostSetupVerify : SimpleWorkflow
    {
        public WFPostSetupVerify()
        {
            Verb = "POST";
            Path = "/Setup/Verify";
        }

        public override void PreProcessImplementation(HttpContext context)
        {
            //read EmailTokenVerification, First, Last, Password, ConfirmPassword, YubikeyOTP (again), DataExchangeAgreement
            var callback = new Func<Stream, Stream, bool>((inputStream, outputStream) =>
            {

                //Parse multi-part request stream, and then rewrite it
                var multipart = new MultipartFormParser.MultipartFormParser();
                var fields = multipart.Parse(inputStream, context.Request.ContentEncoding);

                //Read fields
                var tokenId = fields["VerificationToken"].Value;
                var password = fields["Password"].Value;
                var password2 = fields["Password2"].Value;
                var yubikeyOTP = fields["YubikeyOTP"].Value;

                //Whitelist the following fields to prevent password from being sidebanded.
                string[] whitelistFields = { "ActivationCode", "ClientCode", "FirstName", "LastName", "Email", "Password", "Password2", "DataExchangeAgreement", "VerificationToken" };
                foreach (var key in fields.Keys.ToArray().Where(key => !whitelistFields.Contains(key)))
                {
                    fields.Remove(key);
                }

                var hasErrors = false;
                if (string.IsNullOrEmpty(tokenId))
                {
                    fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                    {
                        Name = "EXTERRMSG",
                        Value = "Missing verification token"
                    });
                    hasErrors = true;
                }
                if (string.IsNullOrEmpty(password))
                {
                    fields.Add("EXTERR_Password", new MultipartFormParser.MultipartFormParser.Field
                    {
                        Name = "EXTERR_Password",
                        Value = "Please provide a password"
                    });
                    hasErrors = true;
                }
                if (string.IsNullOrEmpty(password2))
                {
                    fields.Add("EXTERR_Password2", new MultipartFormParser.MultipartFormParser.Field
                    {
                        Name = "EXTERR_Password2",
                        Value = "Please confirm your password"
                    });
                    hasErrors = true;
                }
                if (!hasErrors && !password.Equals(password2))
                {
                    fields.Add("EXTERR_Password2", new MultipartFormParser.MultipartFormParser.Field
                    {
                        Name = "EXTERR_Password2",
                        Value = "Passwords don't match"
                    });
                    hasErrors = true;
                }
                if (hasErrors)
                {
                    var p = multipart.GetMultipartFormData(fields);
                    outputStream.Write(p, 0, p.Length);

                    return true;
                }

                try
                {
                    var security = new SecurityServiceProxy();
                    var email = security.GetTokenFieldValue(tokenId, "EmailAddress") as string;
                    var clientCode = security.GetTokenFieldValue(tokenId, "ClientCode") as string;

                    if (string.IsNullOrEmpty(email) || string.IsNullOrWhiteSpace(clientCode))
                    {
                        // send up an error to the application
                        fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                        {
                            Name = "EXTERRMSG",
                            Value = "Invalid invitation token"
                        });
                        var p = multipart.GetMultipartFormData(fields);
                        outputStream.Write(p, 0, p.Length);

                        return true;
                    }

                    //Create the admin account
                    //Initialize a new client and create the admin.  This is the only way to create a Client Admin user.
                    var result = security.Initialize(email, password, yubikeyOTP, email, tokenId);


                    if (result.Success)
                    {
                        //Inject password into app
                        fields["Password"].Value = result.Password;
                        fields["Password2"].Value = result.Password;

                        //Override existing email field with email from email validation token.
                        if (fields.ContainsKey("Email"))
                            fields["Email"].Value = email;
                        else
                        {
                            fields.Add("Email", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "Email",
                                Value = email
                            });
                        }

                        //Override existing clientcode field with clientcode from email validation token.
                        if (fields.ContainsKey("ClientCode"))
                            fields["ClientCode"].Value = clientCode;
                        else
                        {
                            fields.Add("ClientCode", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "ClientCode",
                                Value = clientCode
                            });
                        }


                        //Store transactionId for later commit.
                        context.Items["TransactionId"] = result.TransactionId;

                    }
                    else
                    {
                        //Inject password into app
                        fields["Password"].Value = "error";
                        fields["Password2"].Value = "error";
                        //Bubble up an error to the application
                        fields.Add("EXTERRMSG",
                                   new MultipartFormParser.MultipartFormParser.Field
                                       {
                                           Name = "EXTERRMSG",
                                           Value = result.ErrorMessage
                                       });
                    }
                }
                catch (Exception exception)
                {
                    if (exception is PipeException || exception is EndpointNotFoundException)
                    {
                        //If the security service is offline or broken
                        fields["File"].Bytes = new byte[0];
                        if (fields.ContainsKey("EXTERRMSG"))
                        {
                            fields["EXTERRMSG"].Value = SecurityServiceDownMsg;
                        }
                        else
                        {
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERRMSG",
                                    Value = SecurityServiceDownMsg
                                });
                        }
                    }
                    else
                    {
                        if (fields.ContainsKey("EXTERRMSG"))
                        {
                            fields["EXTERRMSG"].Value = exception.Message;
                        }
                        else
                        {
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERRMSG",
                                Value = exception.Message
                            });
                        }
                    }
                }
                //Write request stream
                var post = multipart.GetMultipartFormData(fields);
                outputStream.Write(post, 0, post.Length);

                return true;
            });
            context.Request.Filter = new RequestFilter(context.Request.Filter, callback);

            context.Response.Filter = new ReplaceFilter(context.Response.Filter, context.Response.ContentEncoding,
                new Dictionary<string, string>
                    {
                        {
                            "{{YubikeyOTP}}",
                            @"<label class='control-label' for='YubikeyOTP'>Yubikey</label><input name='YubikeyOTP' type='text' class='form-control'>"
                        }
                    }
                );

        }

        public override void PostProcessImplementation(HttpContext context)
        {
            if (context.Response.StatusCode != 200 && context.Response.StatusCode != 302) return;
            var transactionId = context.Items["TransactionId"];
            if (transactionId == null) return;
            try
            {
                var security = new SecurityServiceProxy();
                security.Commit((Guid)transactionId);
            }
            // ReSharper disable EmptyGeneralCatchClause
            catch
            // ReSharper restore EmptyGeneralCatchClause
            {
                //If the security service is offline or broken the swallow the exception
            }
        }
    }
}
